package com.tool;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import com.sz.dao.impl.MySql;

public class MySQL {
	// 创建jdbc对象
	public static Connection con = null;
	public static Statement st = null;
	public static PreparedStatement ps = null;
	public static ResultSet rs = null;

	// 创建变量
	private static String sqlUrl = null;
	private static String sqlUser = null;
	private static String sqlPwd = null;

	/**
	 * 查询到的数据自动给pojo列表对象序列化。
	 * 
	 * @param obj       pojo对象实例
	 * @param sql       要查寻数据的sql语句
	 * @param parameter 要查寻数据的sql语句的参数
	 * @return 返回序列化后的pojo对象列表，失败返回Null或者空的pojo对象。
	 */
	public static <T> List<T> queryPojoObjectList(T obj, String sql, Object... parameter) {
		List<T> list = new ArrayList<T>();
		// 查询数据并返回记过集
		ResultSet rs = MySQL.query(sql, parameter);
		// 获取ResultSet.class，下面要用到
		Class rsC = ResultSet.class;
		if (rs != null) {// 有数据
			try {
				// 创建传入的obj对象的Class类对象
				Class cla = Class.forName(obj.getClass().getTypeName());
				// 获取传入的obj对象的属性数组
				Field[] f = cla.getDeclaredFields();
				// 光标移到最后一行
				rs.last();
				// 判断行的总数
				int count = rs.getRow();
				// 返回第一行之前
				rs.beforeFirst();
				// System.out.println(count);
				if (count == 0) {
					System.out.println("没有查询到想要的数据...");
					return null;
				} else if (count >= 1) {
					while (rs.next()) {
						Object newobj = cla.newInstance();
						for (Field ff : f) {
							// 判断传入obj对象的属性的数据类型，执行相应的代码块。
							if (ff.getType().equals(int.class) || ff.getType().equals(Integer.class)) {
								// 因为Results对象没有getInteger（），所以要判断一次
								int date = rs.getInt(ff.getName());
								String methodName = "set" + MySQL.upperCase(ff.getName());
								if (ff.getType().equals(Integer.class)) {
									@SuppressWarnings("unchecked")
									Method m = cla.getDeclaredMethod(methodName, Integer.class);
									m.invoke(newobj, date);
								} else {
									@SuppressWarnings("unchecked")
									Method m = cla.getDeclaredMethod(methodName, int.class);
									m.invoke(newobj, date);
								}
							} else {
								// 因为大部分的数据类型都是有完整的包名的（除了int），所以用split方法截取类型字符串。
								String[] s = ff.getType().getName().split("\\.");
								if (s.length >= 0) {
									String str = s[s.length - 1];
									// 获取ResultSet对象实例的方法
									Method rsM = rsC.getDeclaredMethod("get" + str, ff.getType());
									// 获取传入obj对象的属性的set方法名
									String methodName = "set" + MySQL.upperCase(ff.getName());
									// 获取传入obj对象的方法
									@SuppressWarnings("unchecked")
									Method m = cla.getDeclaredMethod(methodName, ff.getType());
									// 执行传入obj对象的方法和ResultSet对象实例的方法
									m.invoke(newobj, rsM.invoke(rs, ff.getName()));
								} else {
									System.out.println("split方法截取类型字符串长度为0，需要重新设计方法。");
								}
							}
						}
						list.add((T) newobj);
					}
				}
			} catch (SecurityException | ClassNotFoundException e) {
				e.printStackTrace();
			} catch (SQLException e) {
				e.printStackTrace();
			} catch (NoSuchMethodException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			} catch (InstantiationException e) {
				e.printStackTrace();
			}
		} else {
			System.out.println("没有查询到数据。");
			return null;
		}

		return list;
	}

	/**
	 * 查询到的数据自动给pojo对象序列化。
	 * 
	 * @param obj       pojo对象实例
	 * @param sql       要查寻数据的sql语句
	 * @param parameter 要查寻数据的sql语句的参数
	 * @return 返回序列化后的pojo对象，失败返回Null或者空的pojo对象。
	 */
	public static Object queryOnePojoObject(Object obj, String sql, Object... parameter) {
		// 查询数据并返回记过集
		ResultSet rs = MySQL.query(sql, parameter);
		// 获取ResultSet.class，下面要用到
		Class rsC = ResultSet.class;
		if (rs != null) {// 有数据
			try {
				// 创建传入的obj对象的Class类对象
				Class cla = Class.forName(obj.getClass().getTypeName());
				// 获取传入的obj对象的属性数组
				Field[] f = cla.getDeclaredFields();
				// 光标移到最后一行
				rs.last();
				// 判断行的总数
				int count = rs.getRow();
				// 返回第一行之前
				rs.beforeFirst();
				// System.out.println(count);
				if (count == 0) {
					System.out.println("没有查询到想要的数据...");
				} else if (count == 1) {
					while (rs.next()) {
						for (Field ff : f) {
							// 判断传入obj对象的属性的数据类型，执行相应的代码块。
							if (ff.getType().equals(int.class) || ff.getType().equals(Integer.class)) {
								// 因为Results对象没有getInteger（），所以要判断一次
								int date = rs.getInt(ff.getName());
								String methodName = "set" + MySQL.upperCase(ff.getName());
								if (ff.getType().equals(Integer.class)) {
									@SuppressWarnings("unchecked")
									Method m = cla.getDeclaredMethod(methodName, Integer.class);
									m.invoke(obj, date);
								} else {
									@SuppressWarnings("unchecked")
									Method m = cla.getDeclaredMethod(methodName, int.class);
									m.invoke(obj, date);
								}
							} else {
								// 因为大部分的数据类型都是有完整的包名的（除了int），所以用split方法截取类型字符串。
								String[] s = ff.getType().getName().split("\\.");
								if (s.length >= 0) {
									String str = s[s.length - 1];
									// 获取ResultSet对象实例的方法
									Method rsM = rsC.getDeclaredMethod("get" + str, ff.getType());
									// 获取传入obj对象的属性的set方法名
									String methodName = "set" + MySQL.upperCase(ff.getName());
									// 获取传入obj对象的方法
									@SuppressWarnings("unchecked")
									Method m = cla.getDeclaredMethod(methodName, ff.getType());
									// 执行传入obj对象的方法和ResultSet对象实例的方法
									m.invoke(obj, rsM.invoke(rs, ff.getName()));
								} else {
									System.out.println("split方法截取类型字符串长度为0，需要重新设计方法。");
								}
							}
						}

					}
				} else {
					System.out.println("有多条记录，该方法执行错误！");
				}
			} catch (SecurityException | ClassNotFoundException e) {
				e.printStackTrace();
			} catch (SQLException e) {
				e.printStackTrace();
			} catch (NoSuchMethodException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			}
		} else {
			System.out.println("没有查询到数据。");
			return null;
		}

		return obj;
	}

	/**
	 * string 首字母大写方法
	 * 
	 * @param str
	 * @return 返回首字母的字符串。
	 */
	public static String upperCase(String str) {
		char[] ch = str.toCharArray();
		if (ch[0] >= 'a' && ch[0] <= 'z') {
			ch[0] = (char) (ch[0] - 32);
		}
		return new String(ch);
	}

	/**
	 * 查询数据库信息并返回结果集
	 * 
	 * 实例："select * from user where uname=?"
	 * 
	 * @param sql       查询语句，为prepareStatement类型
	 * @param parameter 参数，为空或任意多个
	 * @return 返回Resultset类型的结果集，错误的话返回Null
	 */
	public static ResultSet query(String sql, Object... parameter) {
		MySQL.getCon();
		ResultSet rs = null;
		try {
			ps = con.prepareStatement(sql);
			// ps.setMaxRows(10);
			if (parameter.length != 0) {
				for (int i = 0; i < parameter.length; i++) {
					ps.setObject(i + 1, parameter[i]);
				}
			}
			rs = ps.executeQuery();
		} catch (SQLException e) {
			e.printStackTrace();
			System.out.println("query--方法执行失败！！");
			return null;
		}
		return rs;
	}

	/**
	 * 更新或添加数据
	 * 
	 * 实例：1."UPDATE user SET age=18 where uname='李wu'"
	 * 
	 * 2."INSERT INTO user(uname, pwd) value(?, ?)", "李四", "123"
	 * 
	 * 3."inset into user(id,uname, pwd, sex, age, birth) value(default,?,?,?,?,?)"
	 * 
	 * @param sql     查询语句，为prepareStatement类型
	 * @param objects parameter 参数，为空或任意多个
	 * @return 返回int类型的结果,表示成功或失败，错误的话返回0
	 */
	public static int update(String sql, Object... parameter) {
		MySQL.getCon();
		int rs = 0;
		try {
			ps = con.prepareStatement(sql);
			if (parameter.length != 0) {
				for (int i = 0; i < parameter.length; i++) {
					ps.setObject(i + 1, parameter[i]);
				}
			}
			rs = ps.executeUpdate();
		} catch (SQLException e) {
			System.out.println("update--方法执行失败！！");
			e.printStackTrace();
		}
		return rs;
	}
	/**
	 * getPro()方法错误是可以调用该方法。
	 * @param sqlUrl_
	 * @param sqlUser_
	 * @param sqlPwd_
	 */
	public static void create(String sqlUrl_,String sqlUser_, String sqlPwd_) {
		sqlUrl = sqlUrl_;
		sqlUser = sqlUser_;
		sqlPwd = sqlPwd_;
	}

	/**
	 * 获取属性文件 db.properties 的数据库信息
	 * 
	 * @throws IOException
	 */
	public static void getPro() throws IOException {
		InputStream in = MySQL.class.getResourceAsStream("/db.properties");
		Properties p = new Properties();
		p.load(in);
		sqlUrl = p.getProperty("sqlUrl");
		sqlUser = p.getProperty("sqlUser");
		sqlPwd = p.getProperty("sqlPwd");
	}

	// 返回Connection对象的实例
	/**
	 * 获取连接数据库的连接类
	 * 
	 * @return Connection类型
	 */
	public static Connection getCon(String... strings) {
		if (con == null) {
			try {
				MySQL.getPro();
			} catch (IOException e1) {
				e1.printStackTrace();
			}
			try {
				// 加载驱动
				Class.forName("com.mysql.jdbc.Driver");
				// 连接shujuk
				try {
					con = DriverManager.getConnection(sqlUrl, sqlUser, sqlPwd);
				} catch (SQLException e) {
					e.printStackTrace();
				}
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			}
		}
		return con;
	}

	// 关闭连接，//关闭连接,要确认不再使用jdbc后再close，要不然下次使用使会报错，说jdbc为空指针。
	public static void close(Connection con, Statement st, ResultSet rs) throws SQLException {
		rs.close();
		st.close();
		con.close();
		MySql.closeToNull();
	}

	public static void close(Connection con, PreparedStatement ps, ResultSet rs) throws SQLException {
		rs.close();
		ps.close();
		con.close();
		MySql.closeToNull();
	}

	public static void close(Connection con, Statement st) throws SQLException {
		st.close();
		con.close();
		MySql.closeToNull();
	}

	public static void close(Connection con, PreparedStatement ps) throws SQLException {
		ps.close();
		con.close();
		MySql.closeToNull();
	}

	// ----
	public static void close(PreparedStatement ps) throws SQLException {
		ps.close();
		MySql.con.close();
		MySql.closeToNull();
	}

	public static void close(Statement st) throws SQLException {
		ps.close();
		MySql.con.close();
		MySql.closeToNull();
	}

	public static void close(PreparedStatement ps, ResultSet rs) throws SQLException {
		rs.close();
		ps.close();
		MySql.con.close();
		MySql.closeToNull();
	}

	public static void close(Statement st, ResultSet rs) throws SQLException {
		rs.close();
		ps.close();
		MySql.con.close();
		MySql.closeToNull();
	}

	public static void close(ResultSet rs) throws SQLException {
		rs.close();
		ps.close();
		MySql.con.close();
		MySql.closeToNull();
	}


	public static void closeToNull() {
		con = null;
		st = null;
		ps = null;
		rs = null;
	}
}
